﻿using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using HLogger = HK.Core.Logs.Logger;

namespace HK.Core.Utils
{
    /// <summary>
    /// 工具类 : 线程Task相关.
    /// <para>* 使用跟async/await的相关的异步操作</para>
    /// </summary>
    public static class UtilsTask
    {

#if FUNCTION_TESTS
        
        /// <summary>
        /// 时间监控器
        /// </summary>
        private static Stopwatch _taskWatch = new Stopwatch();
        
#endif

        /// <summary>
        /// Task Action委托定义
        /// </summary>
        /// <returns>true:成功; false:失败;</returns>
        public delegate bool TaskAction();
        
        /// <summary>
        /// Task Action委托定义
        /// </summary>
        /// <param name="iSucceeded">成功标志位</param>
        public delegate void TaskCompleted(bool iSucceeded);

        /// <summary>
        /// Task待机校验委托定义
        /// </summary>
        public delegate bool TaskWaitingCheck();
        
        /// <summary>
        /// 运行Task
        /// </summary>
        /// <param name="iAction">欲执行Action</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <returns>Task</returns>
        public static async Task RunTaskAsync(TaskAction iAction, string iTag = null)
        {
#if UNITY_EDITOR
            try
            {
#endif
                // 执行
                var result = await Task.Run(iAction.Invoke);
                var descTag = string.IsNullOrEmpty(iTag) ? $"(Result:{result})" : $"(Tag:{iTag} - {result})";
                if (result)
                {
                    HLogger.Info($"UtilsTask::RunTaskAsync():Succeeded!{descTag}");
                }
                else
                {
                    
                    HLogger.Error($"UtilsTask::RunTaskAsync():Failed!{descTag}");
                }
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal($"UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
        }
        
        /// <summary>
        /// 运行Task
        /// </summary>
        /// <param name="iAction">欲执行Action</param>
        /// <param name="iOnCompleted">完成回调</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <returns>Task</returns>
        public static async Task<bool> RunTaskAsync(
            TaskAction iAction, TaskCompleted iOnCompleted = null, 
            string iTag = null)
        {
            var result = false;
#if UNITY_EDITOR
            try
            {
#endif
                // 执行任务
                result = await Task.Run(iAction.Invoke);
                iOnCompleted?.Invoke(result);
                var descTag = string.IsNullOrEmpty(iTag) ? $"(Result:{result})" : $"(Tag:{iTag} - {result})";
                if (result)
                {
                    HLogger.Info($"UtilsTask::RunTaskAsync():Succeeded!{descTag}");
                }
                else
                {
                    HLogger.Error($"UtilsTask::RunTaskAsync():Completed!{descTag}");
                }
            
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal("UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
            return result;
        }
        
        /// <summary>
        /// 运行Task
        /// </summary>
        /// <param name="iAction">欲执行Action</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <param name="iOnCompleted">完成回调</param>
        /// <returns>Task</returns>
        public static async Task<string> RunTaskAsync(
            Func<string> iAction, string iTag = null, 
            Action<string> iOnCompleted = null)
        {
            string result = null;
#if UNITY_EDITOR
            try
            {
#endif
                // 执行任务
                var task = Task.Run(iAction.Invoke);

                // 等待执行完毕
                await task;
                
                // 返回
                result = task.Result;
                iOnCompleted?.Invoke(result);
                var descTag = string.IsNullOrEmpty(iTag) ? $"(Result:{result})" : $"(Tag:{iTag} - {result})";
                HLogger.Info($"UtilsTask::RunTaskAsync():Completed!{descTag}");
            
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal("UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
            return result;
        }
        
        /// <summary>
        /// 运行Task
        /// </summary>
        /// <param name="iAction">欲执行Action</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <param name="iOnCompleted">完成回调</param>
        /// <returns>Task</returns>
        public static async Task<int> RunTaskAsync(
            Func<int> iAction, string iTag = null, 
            Action<int> iOnCompleted = null)
        {
            var result = -1;
#if UNITY_EDITOR
            try
            {
#endif
            // 执行任务
            var task = Task.Run(iAction.Invoke);

            // 等待执行完毕
            await task;
            
            // 返回
            result = task.Result;
            iOnCompleted?.Invoke(result);
            var descTag = string.IsNullOrEmpty(iTag) ? $"(Result:{result})" : $"(Tag:{iTag} - {result})";
            HLogger.Info($"UtilsTask::RunTaskAsync():Completed!{descTag}");
            
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal("UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
            return result;
        }
        
        /// <summary>
        /// 运行Task
        /// </summary>
        /// <param name="iAction">欲执行Action</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <param name="iOnCompleted">完成回调</param>
        /// <returns>Task</returns>
        public static async Task<float> RunTaskAsync(
            Func<float> iAction, string iTag = null, 
            Action<float> iOnCompleted = null)
        {
            var result = 0.0f;
#if UNITY_EDITOR
            try
            {
#endif
            // 执行任务
            var task = Task.Run(iAction.Invoke);

            // 等待执行完毕
            await task;
            
            // 返回
            result = task.Result;
            iOnCompleted?.Invoke(result);
            var descTag = string.IsNullOrEmpty(iTag) ? $"(Result:{result})" : $"(Tag:{iTag} - {result})";
            HLogger.Info($"UtilsTask::RunTaskAsync():Completed!{descTag}");
            
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal("UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
            return result;
        }
        
        /// <summary>
        /// 运行Task
        /// </summary>
        /// <param name="iAction">欲执行Action</param>
        /// <param name="iWaitCheck">运行状态校验(返回值为true:运行中; false:反之;)</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <returns>Task</returns>
        public static async Task RunTaskAsync(TaskAction iAction, TaskWaitingCheck iWaitCheck, string iTag = null)
        {
#if UNITY_EDITOR
            try
            {
#endif
                // 执行
                await Task.Run(() =>
                {
                    iAction.Invoke();
                    
                    // 若尚在执行中，则待机0.1s
                    while (iWaitCheck())
                    {
                        Thread.Sleep(100);
                    }
                });
            
                var descTag = string.IsNullOrEmpty(iTag) ? "" : $"(Tag:{iTag})";
                HLogger.Info($"UtilsTask::RunTaskAsync():Completed!{descTag}");
            
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal("UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
        }
        
        /// <summary>
        /// 运行Task
        /// </summary>
        /// <param name="iAction">欲执行Task Action</param>
        /// <param name="iWaitCheck">等待校验</param>
        /// <param name="iOnCompleted">完成回调</param>
        /// <param name="iRetry">失败重试次数</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <returns>Task</returns>
        public static async Task RunTaskAsync(
            TaskAction iAction, TaskWaitingCheck iWaitCheck, 
            TaskCompleted iOnCompleted, int iRetry = 3, string iTag = null)
        {
            
#if UNITY_EDITOR
            try
            {
#endif
                var curRetry = iRetry;
                var succeeded = false;
                // 执行
                await Task.Run(() =>
                {
                    while (0 < curRetry)
                    {
                        // 执行Task
                        succeeded = iAction.Invoke();
                    
                        // 若尚在执行中，则待机0.1s
                        while (iWaitCheck())
                        {
                            Thread.Sleep(100);
                        }
                        
                        // 执行成功
                        if (succeeded)
                        {
                            iOnCompleted.Invoke(true);
                            break;
                        }
                        
                        // 尚未成功执行，则重试
                        --curRetry;
                    }

                    // 执行失败
                    if (!succeeded)
                    {
                        iOnCompleted.Invoke(false);
                    }
                    
                });
            
                var descTag = string.IsNullOrEmpty(iTag) ? "" : $"(Tag:{iTag})";
                HLogger.Info($"UtilsTask::RunTaskAsync():Completed!{descTag} Succeeded:{succeeded} Retry:{curRetry}/{iRetry}");
            
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal("UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
        }
        
        /// <summary>
        /// 运行Task
        /// </summary>
        /// <param name="iAction">欲执行Action</param>
        /// <param name="iWaitCheck">待机校验</param>
        /// <param name="iOnCompleted">完成回调</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <returns>Task</returns>
        public static async Task<bool> RunTaskAsync(
            TaskAction iAction, TaskWaitingCheck iWaitCheck, 
            TaskCompleted iOnCompleted = null, string iTag = null)
        {
            var result = false;
#if UNITY_EDITOR
            try
            {
#endif
            // 执行任务
            var task = Task.Run(() =>
            {
                // 若尚在待机中，则待机0.1s
                while (iWaitCheck.Invoke())
                {
                    Thread.Sleep(100);
                }
                
                return iAction.Invoke();
            });

            // 等待执行完毕
            await task;
            
            // 返回
            result = task.Result;
            iOnCompleted?.Invoke(result);
            var descTag = string.IsNullOrEmpty(iTag) ? $"(Result:{result})" : $"(Tag:{iTag} - {result})";
            HLogger.Info($"UtilsTask::RunTaskAsync():Completed!{descTag}");
            
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal("UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
            return result;
        }

        /// <summary>
        /// 运行Task直到指定的条件达成
        /// </summary>
        /// <param name="iWaitCheck">待机校验</param>
        /// <param name="iTag">用以标示执行描述等信息</param>
        /// <returns>Task</returns>
        public static async Task RunTaskUntilAsync(TaskWaitingCheck iWaitCheck, string iTag = null)
        {
#if FUNCTION_TESTS
            _taskWatch.Restart();
            var costTime = 0L;
#endif
#if UNITY_EDITOR
            try
            {
#endif
                // 执行任务
                var task = Task.Run(() =>
                {
                    // 若尚在待机中，则待机0.1s
                    while (iWaitCheck.Invoke())
                    {
                        Thread.Sleep(100);
#if FUNCTION_TESTS
                        costTime += _taskWatch.ElapsedMilliseconds;
                        _taskWatch.Restart();
                    
                        HLogger.Info($"UtilsTask::RunTaskUntilAsync():Waiting(+{costTime} ms)...");
#endif
                    }
                    
#if FUNCTION_TESTS
                    costTime += _taskWatch.ElapsedMilliseconds;
                    _taskWatch.Restart();
                    
                    HLogger.Info($"UtilsTask::RunTaskUntilAsync() +{costTime} ms");
#endif
                });

                // 等待执行完毕
                await task;
                
                // 返回
                var descTag = string.IsNullOrEmpty(iTag) ? "" : $"(Tag:{iTag})";
                HLogger.Info($"UtilsTask::RunTaskUntilAsync():Completed!{descTag}");
#if UNITY_EDITOR
            }
            catch (Exception exp)
            {
                HLogger.Fatal("UtilsTask::RunTaskAsync():Exception Caught!" +
                              $"\n  Exp:{exp.GetType().Name}" +
                              $"\n  Message:{exp.Message}" +
                              $"\n  Source:{exp.Source}" +
                              $"\n  StackTrace:{exp.StackTrace}");
            }
#endif
        }
    }
}
